/*  Copyright © 2009 Blake Hegerle -*- C++ -*- */

/*  This file is part of metal.
 
    Metal is free software: you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.
 
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see
    <http://www.gnu.org/licenses/>.

*/

#include "function.h"
#include "identifier.h"
#include "io.h"

#include <iostream>
#include <cassert>

namespace {
  ident(f);
  ident(fact);
  ident(n);
}

void function_impl_tests() {
  using namespace std;
  using namespace metal;
  using namespace metal::meta;

  typedef meta::bindings<> no_decls;
  typedef meta::bindings<> no_params;

  // nullary function
  auto f_def(f = def() ( 3.14f ));

  auto act_func = convert<no_decls, float>(f_def);

  auto expected_body 
    = imbue_type<constant_expr<float>, no_params>(cnst_impl(3.14f));
  assert(*act_func->body() == *expected_body);
  assert(act_func->name() == "f");
  assert(act_func->begin() == act_func->end());

  // fact 

  typedef bindings<
    binding<meta_n, int>, 
    binding<meta_fact, metal::list<int, int> > 
  > fact_scope;
    
  auto fact_def = (fact = def(param(int, n)) 
		   (cond(n < 2, 1, n * fact(n - 1))));
  act_func = convert<fact_scope, int>(fact_def);

  typedef cond_expr<
    binary_expr<comparison_tag, ident_expr<meta_n>, constant_expr<int> >, 
      constant_expr<int>, 
      binary_expr<arithmatic_tag, 
        ident_expr<meta_n>, 
        call_expr<ident_expr<meta_fact>, 
        binary_expr<arithmatic_tag, 
          ident_expr<meta_n>, 
          constant_expr<int> 
        > 
      > 
    > 
  > fact_meta_expr;

  expected_body
    = imbue_type<fact_meta_expr, fact_scope>
    (cond(n < 2, 1, n * fact(n - 1)).impl());

  assert(*act_func->body() == *expected_body);
  assert(act_func->name() == "fact");

  {
    auto i = act_func->begin();
    auto e = act_func->end();
    assert(i != e);
    auto param = *i;
    assert(++i == e);
  }

  cout << "function ok" << endl;
}
